import numpy as np
클래스공부 4단계
Motivating Example
-
가위바위보
- 방법1
class RPC2:
def throw2(self):
print(np.random.choice(['가위', '바위','보']))
=RPC2() a
a.throw2()
바위
- 방법2
class RPC:
def thorw(self, candidate):
print(np.random.choice(candidate))
=RPC() a
'가위','바위','보']) a.thorw([
바위
- 방법3
class RPC3:
def __init__(self, candidate=['가위','바위','보']):
self.candidate = candidate
def throw3(self):
print(np.random.choice(self.candidate))
=RPC3() # __init__ 는 암묵적으로 실행 a
a.throw3()
바위
- 방법4
class RPC4:
pass
=RPC4() b
def initt(b, candidate=['가위','바위','보']):
= candidate b.candidate
initt(b)
b.candidate
['가위', '바위', '보']
def throww(b):
print(np.random.choice(b.candidate))
throww(b)
가위
- 방법5
# 위의 코드를 하나로 합치면..
class RPC4:
def __init__(self, candidate=['가위','바위','보']):
self.candidate = candidate
def throww(self):
print(np.random.choice(self.candidate))
=RPC4() a
a.throww()
가위
-
생각해보니까 throw는 choose + show 의 결합인 것 같다.
class RPC:
def __init__(self, candidate=['가위','바위','보']):
self.candidate = candidate
def choose(self):
self.actions = np.random.choice(self.candidate)
def show(self):
print(self.actions)
=RPC() a
# 지금은 정의되지 않음 a.actions()
AttributeError: 'RPC' object has no attribute 'actions'
a.choose()
# 가위, 바위, 보 중 고른 결과가 나옴 a.actions
'가위'
a.show()
가위
보충학습 : 위와 같은 코드
class _RPS: ## 시점1
pass # <- 이렇게하면 아무기능이 없는 비어있는 클래스가 정의된다
= _RPS() ## 시점2
_a def _init(_a,candidate=['가위','바위','보']):
= candidate
_a.candidate _init(_a)
## 시점3 _a.actions
AttributeError: '_RPS' object has no attribute 'actions'
def _choose(_a): ## 시점4
= np.random.choice(_a.candidate)
_a.actions _choose(_a)
## 시점5 _a.actions
'보'
def _show(_a): ## 시점6
print(_a.actions)
_show(_a)
보
-
또 다른 인스턴스 b를 만들자. b는 가위만 낼 수 있다.
RPC?
Init signature: RPC(candidate=['가위', '바위', '보']) Docstring: <no docstring> Type: type Subclasses:
=RPC(['가위']) b
b.candidate
['가위']
b.choose() b.show()
가위
-
a,b의 선택들을 모아서 기록을 하고 싶다.
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1]) # 지금 현재 내가 선택한 마지막만 보여줘!
=RPS()
a=RPS(['가위']) b
for i in range(5):
a.choose() a.show()
바위
가위
바위
바위
보
# 지금까지 뽑힌 히스토리들 a.actions
['바위', '가위', '바위', '바위', '보']
for i in range(5):
b.choose() b.show()
가위
가위
가위
가위
가위
b.actions
['가위', '가위', '가위', '가위', '가위']
a.candidate, a.actions
(['가위', '바위', '보'], ['바위', '가위', '바위', '바위', '보'])
b.candidate, b.actions
(['가위'], ['가위', '가위', '가위', '가위', '가위'])
-
info라는 함수를 만들어서 a의 오브젝트가 가지고 있는 정보를 모두 보도록 하자.
(예비학습) 문자열 \n
이 포함된다면?
'asdf\n1234'
'asdf\n1234'
print('asdf\n1234')
asdf
1234
예비학습 끝
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def info(self):
print("낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions))
=RPS()
a=RPS(['가위']) b
for i in range(5):
a.choose() a.show()
가위
바위
가위
보
보
for i in range(5):
b.choose() b.show()
가위
가위
가위
가위
가위
a.info()
낼 수 있는 패: ['가위', '바위', '보']
기록: ['가위', '바위', '가위', '보', '보']
b.info()
낼 수 있는 패: ['가위']
기록: ['가위', '가위', '가위', '가위', '가위']
-
만들고 보니까 info와 print의 기능이 거의 비슷함 \(\to\) print(a)를 하면 a.info()와 동일한 효과를 내도록 만들 수 있을까?
-
안될 거 같다. 왜?
안될 것 같은 이유 1: print 는 파이썬 내장기능, 내장기능을 우리가 맘대로 커스터마이징해서 쓰기는 어려울 것 같다.
안될 것 같은 이유 2: 이유1이 해결된다고 해도 문제다. 다 꼬아져버려… 그럼 지금까지 사용했던 print()의 결과는 어떻게 되는가?
(예)
type(a)
__main__.RPS
a?
Type: RPS String form: <__main__.RPS object at 0x7f6565f29e10> Docstring: <no docstring>
-
그런데 a의 자료형(RPS자료형)에 해당하는 오브젝트들에 한정하여 print를 수정하는 방법이 가능하다면? (그럼 다른 오브젝트들은 수정된 print에 영향을 받지 않음)
__str___
-
관찰1: 현재 print(a)의 결과는 아래와 같다.
print(a)
<__main__.RPS object at 0x7f6565f29e10>
print([1,2,3])
[1, 2, 3]
- a는 RPS클래스에서 만든 오브젝트이며 a가 저장된 메모리 주소는 0x7f6565f29e10라는 의미
-
관찰2: a에는 __str__
이 있다.
dir(a) # a + _ + tab을 누르면 숨겨진 메소드들이 나온다.
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'actions',
'candidate',
'choose',
'info',
'show']
set(dir(a)) & {'__str__'}
{'__str__'}
__str__ a.
<method-wrapper '__str__' of RPS object at 0x7f6565f29e10>
이것을 함수처럼 사용하니까 아래와 같이 된다.
__str__() a.
'<__main__.RPS object at 0x7f6565f29e10>'
?? print(a)를 해서 나오는 문자열이 리턴된다..
print(a.__str__()) # 이거 print(a)를 실행한 결과와 같다?
<__main__.RPS object at 0x7f6565f29e10>
-
생각: 만약 내가 a.__str__()
라는 함수를 재정의하여 리턴값을 boram hahaha
로 바꾸게 되면 print(a)해서 나오는 결과는 어떻게 될까? (해커???)
(예비학습)
def f():
print('adsf')
f()
adsf
def f():
print('boram hahaha')
f()
boram hahaha
이런식으로 함수가 이미 정의되어 있더라도, 내가 나중에 덮어씌우면 그 함수의 기능을 다시 정의한다.
(해킹시작)
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return 'boram hahaha'
def info(self):
print("낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions))
=RPS() a
print(a)
boram hahaha
print(a.__str__())
boram hahaha
# 다른건 다 변함이 없음
a.choose() a.show()
가위
a.actions
['가위']
a.info()
낼 수 있는 패: ['가위', '바위', '보']
기록: ['가위']
-
__str__
의 리턴값을 info에서 타이핑했던 문자열로 재정의 한다면?
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
=RPS() a
print(a)
낼 수 있는 패: ['가위', '바위', '보']
기록: []
a.choose() a.show()
바위
print(a)
낼 수 있는 패: ['가위', '바위', '보']
기록: ['보', '바위']
파이썬의 비밀2
-
print(a)
와 print(a.__str__())
는 같은 문법이다.
-
참고로 a.__Str__()
와 str(a)
도 같은 문법
__str__() a.
"낼 수 있는 패: ['가위', '바위', '보']\n기록: ['보', '바위']"
str(a)
"낼 수 있는 패: ['가위', '바위', '보']\n기록: ['보', '바위']"
-
지금까지 썼던 기능들 확인!
(예제1)
=[1,2,3]
aprint(a)
[1, 2, 3]
__str__() a.
'[1, 2, 3]'
str(a)
'[1, 2, 3]'
(예제2)
={1,2,3}
aprint(a)
{1, 2, 3}
__str__() a.
'{1, 2, 3}'
str(a)
'{1, 2, 3}'
(예제3)
=np.array(1)
a a.shape
()
type(a.shape)
tuple
print(a.shape)
()
__str__() a.shape.
'()'
str(a.shape)
'()'
(예제4)
= range(10)
a print(a)
range(0, 10)
__str__() a.
'range(0, 10)'
(예제5)
= np.arange(100).reshape(10,10)
a print(a)
[[ 0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24 25 26 27 28 29]
[30 31 32 33 34 35 36 37 38 39]
[40 41 42 43 44 45 46 47 48 49]
[50 51 52 53 54 55 56 57 58 59]
[60 61 62 63 64 65 66 67 68 69]
[70 71 72 73 74 75 76 77 78 79]
[80 81 82 83 84 85 86 87 88 89]
[90 91 92 93 94 95 96 97 98 99]]
__str__() a.
'[[ 0 1 2 3 4 5 6 7 8 9]\n [10 11 12 13 14 15 16 17 18 19]\n [20 21 22 23 24 25 26 27 28 29]\n [30 31 32 33 34 35 36 37 38 39]\n [40 41 42 43 44 45 46 47 48 49]\n [50 51 52 53 54 55 56 57 58 59]\n [60 61 62 63 64 65 66 67 68 69]\n [70 71 72 73 74 75 76 77 78 79]\n [80 81 82 83 84 85 86 87 88 89]\n [90 91 92 93 94 95 96 97 98 99]]'
str(a)
'[[ 0 1 2 3 4 5 6 7 8 9]\n [10 11 12 13 14 15 16 17 18 19]\n [20 21 22 23 24 25 26 27 28 29]\n [30 31 32 33 34 35 36 37 38 39]\n [40 41 42 43 44 45 46 47 48 49]\n [50 51 52 53 54 55 56 57 58 59]\n [60 61 62 63 64 65 66 67 68 69]\n [70 71 72 73 74 75 76 77 78 79]\n [80 81 82 83 84 85 86 87 88 89]\n [90 91 92 93 94 95 96 97 98 99]]'
__repr__
-
생각해보니까 print를 해서 원하는 정보를 확인하는 건 아니었음
=[1,2,3] a
a
[1, 2, 3]
print(a) # print(a.__str__()) + enter => a + enter 와 같은 효과?
[1, 2, 3]
-
a + 엔터
를 하면 print(a) + 엔터
를 하는 것과 같은 효과인가?
(반례)
=np.array([1,2,3,4]).reshape(2,2) a
a
array([[1, 2],
[3, 4]])
print(a)
[[1 2]
[3 4]]
-
a + 엔터
를 하면 print(a) + 엔터
가 다른 경우도 있다. \(\to\) 서로 다른 숨겨진 기능이 잇다! \(\to\) 결론 : 그 기능은 __repr__
에 저장되어 있다.
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
=RPS() a
# print(a.__repr__()) a
낼 수 있는 패: ['가위', '바위', '보']
기록: []
-
그럼 지금까지 한것은?
= np.array([1,2,3]) a
a
array([1, 2, 3])
print(a)
[1 2 3]
__repr__() a.
'array([1, 2, 3])'
__str__() a.
'[1 2 3]'
파이썬의 비밀3
-
대화형 콘솔에서 오브젝트 이름 + 엔터
를 쳐서 나오는 출력은 __repr__
의 결과와 연관 있다.
= np.array(range(10000)).reshape(100,100) a
a
array([[ 0, 1, 2, ..., 97, 98, 99],
[ 100, 101, 102, ..., 197, 198, 199],
[ 200, 201, 202, ..., 297, 298, 299],
...,
[9700, 9701, 9702, ..., 9797, 9798, 9799],
[9800, 9801, 9802, ..., 9897, 9898, 9899],
[9900, 9901, 9902, ..., 9997, 9998, 9999]])
__repr__() a.
'array([[ 0, 1, 2, ..., 97, 98, 99],\n [ 100, 101, 102, ..., 197, 198, 199],\n [ 200, 201, 202, ..., 297, 298, 299],\n ...,\n [9700, 9701, 9702, ..., 9797, 9798, 9799],\n [9800, 9801, 9802, ..., 9897, 9898, 9899],\n [9900, 9901, 9902, ..., 9997, 9998, 9999]])'
-
참고로 a.__repr__()
은 representation의 약자인데, repr(a)
와 같다.
주피터 노브북의 비밀 (__repr__html__)
-
요즘에는 IDE의 발전에 따라서 오브젝트이름+엔터
칠때 나오는 출력의 형태도 다양해지고 있다.
import pandas as pd
= pd.DataFrame({'a':[1,2,3],'b':[2,3,4]}) df
df
a | b | |
---|---|---|
0 | 1 | 2 |
1 | 2 | 3 |
2 | 3 | 4 |
- 예쁘게 나온다.
-
그런데? print(df.__repr__())
의 결과가 조금 다르게 나온당
print(df.__repr__())
a b
0 1 2
1 2 3
2 3 4
-
print(df.__repr__())
는 예전 검은화면에서 코딩할 때가 나오는 출력임
3.10.2 | packaged by conda-forge | (main, Feb 1 2022, 19:28:35) [GCC 9.4.0] on linux
Python "help", "copyright", "credits" or "license" for more information.
Type > >> import pandas as pd
>>> df = pd.DataFrame({'a':[1,2,3],'b':[2,3,4]})>>> df
a b0 1 2
1 2 3
2 3 4
>>>
-
주피터에서는? “오브젝트이름+엔터”치면 HTML(df._repr_html_())
이 실행되고, _repr_html_()
이 정의되어 있지 않으면 print(df.__repr__())
이 실행된다.
df._repr_html_()
'<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border="1" class="dataframe">\n <thead>\n <tr style="text-align: right;">\n <th></th>\n <th>a</th>\n <th>b</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>1</td>\n <td>2</td>\n </tr>\n <tr>\n <th>1</th>\n <td>2</td>\n <td>3</td>\n </tr>\n <tr>\n <th>2</th>\n <td>3</td>\n <td>4</td>\n </tr>\n </tbody>\n</table>\n</div>'
- html코드
from IPython.core.display import HTML
'<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border="1" class="dataframe">\n <thead>\n <tr style="text-align: right;">\n <th></th>\n <th>a</th>\n <th>b</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>1</td>\n <td>2</td>\n </tr>\n <tr>\n <th>1</th>\n <td>2</td>\n <td>3</td>\n </tr>\n <tr>\n <th>2</th>\n <td>3</td>\n <td>4</td>\n </tr>\n </tbody>\n</table>\n</div>') HTML(
a | b | |
---|---|---|
0 | 1 | 2 |
1 | 2 | 3 |
2 | 3 | 4 |
HTML(df._repr_html_())
a | b | |
---|---|---|
0 | 1 | 2 |
1 | 2 | 3 |
2 | 3 | 4 |
-
물론 df._repr_html_()
함수가 내부적으로 있어도 html이 지원되지 않는 환경이라면 print(__repr__())
이 내부적으로 수행된다.
__repr__
와 __str__
의 우선적용 순위
(예제1)
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
=RPS()
a a
낼 수 있는 패: ['가위', '바위', '보']
기록: []
__repr__() a.
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
repr(a)
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
-
여기까지는 상식수준의 결과. 아래 관찰하자
print(a) # print(a.__str__())
낼 수 있는 패: ['가위', '바위', '보']
기록: []
__str__() a.
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
str(a)
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
-
__str__()
은 건드린적이 없다…?
__repr__?? a.
Signature: a.__repr__() Docstring: Return repr(self). Source: def __repr__(self): return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions) File: ~/Dropbox/coco/posts/python/<ipython-input-201-29baf6ff56bf> Type: method
얘는 건드림
__str__?? a.
Signature: a.__str__() Call signature: a.__str__(*args, **kwargs) Type: method-wrapper String form: <method-wrapper '__str__' of RPS object at 0x7f6561614c10> Docstring: Return str(self).
얘는 안건드렸는디..
(예제2)
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
=RPS() a
print(a)
낼 수 있는 패: ['가위', '바위', '보']
기록: []
a
<__main__.RPS at 0x7f6561429950>
__str__() a.
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
__repr__() a.
'<__main__.RPS object at 0x7f6561429950>'
__str__?? a.
Signature: a.__str__() Docstring: Return str(self). Source: def __str__(self): return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions) File: ~/Dropbox/coco/posts/python/<ipython-input-214-cd2a21868510> Type: method
a.__repr_??
Object `a.__repr_` not found.
(예제3)
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "haha"
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
=RPS() a
a
haha
print(a)
낼 수 있는 패: ['가위', '바위', '보']
기록: []
-
__str__
와 __repr__
을 건드리지 않고 출력결과를 바꾸고 싶다면?
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def _repr_html_(self):
= """
html_str 낼 수 있는 패: {} <br/>
기록: {}
"""
return html_str.format(self.candidate,self.actions)
=RPS() a
a
기록: []
print(a)
<__main__.RPS object at 0x7f6561534410>
str(a)
'<__main__.RPS object at 0x7f6561534410>'
repr(a)
'<__main__.RPS object at 0x7f6561534410>'
for i in range(5):
a.choose() a.show()
바위
바위
바위
가위
보
a
기록: ['바위', '바위', '바위', '가위', '보']